package com.atom.module.lib.opengles.egl

import android.opengl.*

object EGLHelper {

    fun initDisplay(): EGLWrapper {
        return initDisplay(EGL14.EGL_DEFAULT_DISPLAY)
    }

    /**
     * 初始化显示器 display
     */
    fun initDisplay(key: Int): EGLWrapper {
        val display = EGL14.eglGetDisplay(key)
        val versions = IntArray(2)//获取版本号，[0]为版本号，[1]为子版本号
        EGL14.eglInitialize(display, versions, 0, versions, 1)
        return EGLWrapper(key, versions[0], versions[1], display)
    }

    /**
     * 获取EGL的config
     */
    fun getConfig(egl: EGLWrapper, attrs: EGLConfigAttrs) {
        val config = getConfig(egl.eglDisplay, attrs)
        if (config != null) {  //选择的过程可能出现多个，也可能一个都没有，这里只用一个
            egl.eglConfig = config
            return
        }
        throw EGLException("getConfig ${egl} \n")
    }

    fun getConfig(display: EGLDisplay, attrs: EGLConfigAttrs): EGLConfig? {
        val configs = arrayOfNulls<EGLConfig>(1)
        val configNum = IntArray(1)
        EGL14.eglChooseConfig(
            display, attrs.build(), 0,
            configs, 0, 1,
            configNum, 0
        )
        if (configNum[0] > 0) {  //选择的过程可能出现多个，也可能一个都没有，这里只用一个
            return configs[0]
        }
        return null
    }

    fun querySurfaceWidth(egl: EGLWrapper): Int {
        val value = IntArray(1)
        EGL14.eglQuerySurface(egl.eglDisplay, egl.eglSurface, EGL14.EGL_WIDTH, value, 0)
        return value[0]
    }

    fun querySurfaceHeight(egl: EGLWrapper): Int {
        val value = IntArray(1)
        EGL14.eglQuerySurface(egl.eglDisplay, egl.eglSurface, EGL14.EGL_HEIGHT, value, 0)
        return value[0]
    }

    /**
     * 创建EGL context
     */
    fun createContext(
        egl: EGLWrapper,
        attrs: EGLContextAttrs
    ) {
        val context = createContext(egl.eglDisplay, egl.eglConfig, EGL14.EGL_NO_CONTEXT, attrs)
        if (context != EGL14.EGL_NO_CONTEXT) {
            egl.eglContext = context
            return
        }
        throw EGLException("createContext ${egl} \n")
    }

    fun createContext(
        display: EGLDisplay,
        config: EGLConfig?,
        share: EGLContext,
        attrs: EGLContextAttrs
    ): EGLContext {
        return EGL14.eglCreateContext(display, config, share, attrs.build(), 0)
    }

    /**
     * 创建一个window surface
     */

    fun createWindowSurface(egl: EGLWrapper, surface: Any) {
        val eglSurface = createWindowSurface(egl.eglDisplay, egl.eglConfig, surface)
        if (eglSurface != EGL14.EGL_NO_SURFACE) {
            egl.setSurface(eglSurface, EGLWrapper.SurfaceType.Window)
            return
        }
        throw EGLException("createWindowSurface ${egl} \n")
    }

    fun createWindowSurface(display: EGLDisplay, config: EGLConfig?, surface: Any): EGLSurface {
        return EGL14.eglCreateWindowSurface(
            display,
            config,
            surface,
            intArrayOf(EGL14.EGL_NONE),
            0
        )
    }

    /**
     * 创建一个 后台 surface
     */
    fun createPBufferSurface(
        egl: EGLWrapper,
        width: Int,
        height: Int
    ) {
        val surface =
            createPBufferSurface(egl.eglDisplay, egl.eglConfig, width, height)
        if (surface != EGL14.EGL_NO_SURFACE) {
            egl.setSurface(surface, EGLWrapper.SurfaceType.Offscreen)
            return
        }
        throw EGLException("createPBufferSurface ${egl} \n")
    }

    fun createPBufferSurface(
        display: EGLDisplay,
        config: EGLConfig?,
        width: Int,
        height: Int
    ): EGLSurface {
        return EGL14.eglCreatePbufferSurface(
            display,
            config,
            intArrayOf(
                EGL14.EGL_WIDTH, width,
                EGL14.EGL_HEIGHT, height, EGL14.EGL_NONE
            ),
            0
        )
    }

    /**
     * make  Current
     */
    fun makeCurrent(
        display: EGLDisplay,
        draw: EGLSurface,
        read: EGLSurface,
        context: EGLContext
    ): Boolean {
        if (display == EGL14.EGL_NO_DISPLAY) return false
        if (draw == EGL14.EGL_NO_SURFACE) return false
        if (read == EGL14.EGL_NO_DISPLAY) return false
        if (context == EGL14.EGL_NO_CONTEXT) return false
        return EGL14.eglMakeCurrent(display, draw, read, context)
    }

    fun makeCurrent(egl: EGLWrapper): Boolean {
        return makeCurrent(egl.eglDisplay, egl.eglSurface, egl.eglSurface, egl.eglContext)
    }

    fun makeNothingCurrent(egl: EGLWrapper) {
        if (!makeCurrent(
                egl.eglDisplay,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_SURFACE,
                egl.eglContext
            )
        ) {
            throw RuntimeException("eglMakeCurrent failed")
        }
    }

    fun isSurfaceCurrent(egl: EGLWrapper, witch: Int = EGL14.EGL_DRAW): Boolean {
        return isContextCurrent(egl)
                && egl.eglSurface == EGL14.eglGetCurrentSurface(witch)
    }

    fun isContextCurrent(egl: EGLWrapper): Boolean {
        return egl.eglContext == EGL14.eglGetCurrentDisplay()
    }

    fun isDisplayCurrent(egl: EGLWrapper): Boolean {
        return egl.eglDisplay == EGL14.eglGetCurrentDisplay()
    }

    fun setPresentationTime(display: EGLDisplay, surface: EGLSurface?, time: Long) {
        EGLExt.eglPresentationTimeANDROID(display, surface, time)
    }

    fun setPresentationTime(eglWrapper: EGLWrapper, time: Long) {
        setPresentationTime(eglWrapper.eglDisplay, eglWrapper.eglSurface, time)
    }


    /**
     * 创建 EGL config context surface
     */
    fun createGLESWithSurface(
        egl: EGLWrapper,
        attrs: EGLConfigAttrs,
        ctxAttrs: EGLContextAttrs,
        surface: Any
    ): Boolean {
        try {
            // init config
            getConfig(egl, attrs.surfaceType(EGL14.EGL_WINDOW_BIT))
            // init context
            createContext(egl, ctxAttrs)
            // init surface
            createWindowSurface(egl, surface)
            // init makeCurrent
            if (!makeCurrent(egl)) {
                throw EGLException("EGL Make Current ERROR")
            }
            return true
        } catch (e: Exception) {
            if (e is EGLException) {
                getError(e.message)
            }
            egl.reset()
            return false
        }
    }

    /**
     * 创建一个后台EGL surface
     * 和 window surface 的不同的是, 该surface 可以配置其宽高
     */
    fun createGLESWithPBuffer(
        egl: EGLWrapper,
        attrs: EGLConfigAttrs,
        ctxAttrs: EGLContextAttrs,
        width: Int,
        height: Int
    ): Boolean {
        try {
            // init config
            getConfig(egl, attrs.surfaceType(EGL14.EGL_PBUFFER_BIT))
            // init context
            createContext(egl, ctxAttrs)
            // init surface
            createPBufferSurface(egl, width, height)
            // init makeCurrent
            if (!makeCurrent(egl)) {
                throw EGLException("EGL Make Current ERROR")
            }
            return true
        } catch (e: Exception) {
            if (e is EGLException) {
                getError(e.message)
            }
            egl.reset()
            return false
        }
    }

    fun swapBuffers(egl: EGLWrapper): Boolean {
        return EGL14.eglSwapBuffers(egl.eglDisplay, egl.eglSurface)
    }

    fun destroy(egl: EGLWrapper) {
        if (egl.eglDisplay != EGL14.EGL_NO_DISPLAY) {
            EGL14.eglMakeCurrent(
                egl.eglDisplay,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_SURFACE,
                EGL14.EGL_NO_CONTEXT
            )
            if (egl.eglSurface != EGL14.EGL_NO_SURFACE) {
                EGL14.eglDestroySurface(egl.eglDisplay, egl.eglSurface)
            }
            if (egl.eglContext != EGL14.EGL_NO_CONTEXT) {
                EGL14.eglDestroyContext(egl.eglDisplay, egl.eglContext)
            }
            EGL14.eglReleaseThread()
            EGL14.eglTerminate(egl.eglDisplay)
        }
    }

    fun destroySurface(egl: EGLWrapper) {
        if (egl.eglDisplay != EGL14.EGL_NO_DISPLAY) {
            EGL14.eglDestroySurface(egl.eglDisplay, egl.eglSurface)
        }
    }

    private fun getError(opName: String?): String {
        val eglGetError = EGL14.eglGetError()
        if (eglGetError != EGL14.EGL_SUCCESS) {
            return "Error during $opName: EGL error 0x${Integer.toHexString(eglGetError)}"
        }
        return "EGL_SUCCESS"
    }

}